package magnet.processor.instances.aspects.siblings;

import com.squareup.javapoet.*;
import magnet.processor.instances.GetSiblingTypesMethod;
import magnet.processor.instances.generator.AspectGenerator;

import javax.lang.model.element.Modifier;

public class GetSiblingTypesMethodGenerator implements AspectGenerator {

    private MethodSpec getSiblingTypes = null;
    private FieldSpec constBuilder = null;
    private CodeBlock.Builder constInitializer = null;
    private int typesLeft = 0;

    public void enterSiblingTypesMethod(GetSiblingTypesMethod method) {
        getSiblingTypes = MethodSpec
                .methodBuilder("getSiblingTypes")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(Override.class)
                .returns(ArrayTypeName.of(Class.class))
                .addStatement("return SIBLING_TYPES")
                .build();
        constInitializer = CodeBlock.builder().add("{ ");
        typesLeft = method.getSiblingTypes().size();
    }

    public void visitSiblingType(ClassName type) {
        if (--typesLeft > 0) {
            checkNotNull(constInitializer).add("$T.class, ", type);
        } else {
            checkNotNull(constInitializer).add("$T.class }", type);
        }
    }

    public void exitSiblingTypesMethod() {
        constBuilder = FieldSpec
                .builder(ArrayTypeName.of(Class.class), "SIBLING_TYPES")
                .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
                .initializer(checkNotNull(constInitializer).build())
                .build();
    }

    @Override
    public void generate(TypeSpec.Builder classBuilder) {
        if (constBuilder != null) {
            classBuilder.addField(constBuilder);
        }
        if (getSiblingTypes != null) {
            classBuilder.addMethod(getSiblingTypes);
        }
    }

    @Override
    public void reset() {
        getSiblingTypes = null;
        constBuilder = null;
        constInitializer = null;
        typesLeft = 0;
    }

    private <T> T checkNotNull(T value) {
        if (value == null) {
            throw new IllegalArgumentException("Required value was null.");
        }
        return value;
    }
}
